package com.irenty.effectivej.ch02.objectreation;

import java.math.BigInteger;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.logging.Logger;

import org.testng.annotations.Test;

import com.irenty.effectivej.App;
import com.irenty.effectivej.ch02.objectcreation.serviceproviderframework.MyService1Provider;
import com.irenty.effectivej.ch02.objectcreation.serviceproviderframework.MyService2Provider;
import com.irenty.effectivej.ch02.objectcreation.serviceproviderframework.Services;

/*
 * Item 1: Consider static factory methods instead of constructors
 */
public class StaticFactoryTest {
	private final static Logger LOGGER = Logger.getLogger(App.class.getName());
	
	@Test
	/*
	 * 2. Static factory methods are not required to create new object each time. Flyweight pattern.
	 * The class controls the instance creation. == operator can be used instead of equals method.
	 */
	public void testBooleans() throws Exception {
		LOGGER.info("boolean true = " + Boolean.valueOf(true).toString());
	}
	
	@Test
	/*
	 * 2. Static factory methods are not required to create new object each time. Flyweight pattern.
	 * 
	 */
	public void testBigIntegerValueOf() throws Exception {
		BigInteger i1 = BigInteger.valueOf(15);
		BigInteger i2 = BigInteger.valueOf(15);
		LOGGER.info("BigInteger.valueOf(15) == BigInteger.valueOf(15) is " + Boolean.valueOf(i1 == i2));
		
		BigInteger i3 = BigInteger.valueOf(19);
		BigInteger i4 = BigInteger.valueOf(19);
		LOGGER.info("BigInteger.valueOf(19) == BigInteger.valueOf(19) is " + Boolean.valueOf(i3 == i4));
		
		LOGGER.info("Only first 16 of BigIneters are cached");
	}
	
	@Test
	/*
	 * 1. Static factory methods unlike constructors have names
	 */
	public void testBigIntegerProbablePrime() throws Exception {
		LOGGER.info("BigInteger.probablePrime is " + BigInteger.probablePrime(20, new Random(332)));
	}
	
	private enum MyEnum {
		ONE, THREE, FOUR;
	}
	
	@Test
	/*
	 * 3. Static factory methods can return subtypes depending on the parameter.
	 * Constructor can create only the constructed class;
	 */
	public void testEnumSet() throws Exception {
		EnumSet<MyEnum> myEnumSet = EnumSet.allOf(MyEnum.class);
		LOGGER.info("EnumSet class is " + myEnumSet.getClass().getName());
	}
	
	@Test
	/*
	 * 3. Static factory are useful to create service provider frameworks.
	 * Example is JDBC
	 */
	public void testServiceProvidedInterface() throws Exception {
		Services.registerDefaultProvider(new MyService1Provider());
		Services.registerProvider("custom", new MyService2Provider());
		
		Services.newInstance().doSomething();
		
		Services.newInstance("custom").doSomething();
	}
	
	private static <K, V> HashMap<K, V> newInstance() {
		return new HashMap<K, V>();
		}

	
	@Test
	/*
	 * 4. Static factory reduces the verbosity of the parametrized types
	 */
	public void testParametrizedTypes() throws Exception {
		Map<String, Integer> strToInt = StaticFactoryTest.newInstance();
		strToInt.put("123", 123);
		
		LOGGER.info("value for 123 is " + strToInt.get("123"));
	}
	
	/*
	 * 5. Disadvantages
	 * - you can't extend the class without public or protected constructor
	 * - hard to distinguish static factories from other methods in the javadocs
	 */
	
}
